﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Web;
using System.Threading;
using System.Xml.Linq;
using System.Net;
using System.Text;
using System.IO;
using AMicroblogAPI;
using AMicroblogAPI.Common;

namespace WeiboPushService
{
    public class InternalPushService
    {
        public bool RegisterOrChange(UserProfile userProfile)
        {
            TaskEntry entry = null;

            if (!users.TryGetValue(userProfile.UserID, out entry))
            {
                entry = new TaskEntry();

                if (!users.TryAdd(userProfile.UserID, entry))
                {
                    return false;
                }
            }

            //if (users.ContainsKey(userProfile.UserID))
            //{
            //    entry = users[userProfile.UserID];
            //}
            //else
            //{
            //    entry = new TaskEntry();
            //    users[userProfile.UserID] = entry;
            //}
            
            entry.Update(userProfile);

            return true;
        }

        public void UnRegister(string userID)
        {
            TaskEntry entry = null;

            if (users.TryRemove(userID, out entry))
            {
                entry.PrepareToDie();
            }

            //if (users.ContainsKey(userID))
            //{
            //    users[userID].PrepareToDie();
            //    users.Remove(userID);
            //}
        }

        public bool IsRegistered(string userID)
        {
            return this.users.ContainsKey(userID);
        }


        public static InternalPushService Singleton = new InternalPushService();

        #region private

        private InternalPushService()
        {
            this.users = new ConcurrentDictionary<string, TaskEntry>();
        }

        private ConcurrentDictionary<string, TaskEntry> users;

        #endregion

    }

    public enum TaskStatus { OnSchedule, ToBeRemove };

    public sealed class TaskEntry
    {
        public TaskEntry()
        {
            syncRoot = new object();
            timer = new Timer(Run, this, TimeSpan.FromTicks(-1), TimeSpan.FromTicks(-1));
        }

        public void PrepareToDie()
        {
            Monitor.Enter(syncRoot);
            try
            {
                timer.Change(TimeSpan.FromMilliseconds(-1), TimeSpan.FromMilliseconds(-1));
                Status = TaskStatus.ToBeRemove;
            }
            finally
            {
                Monitor.Exit(syncRoot);
            }
        }

        public void Update(UserProfile user)
        {
            Monitor.Enter(syncRoot);
            try
            {
                User = user;

                if (user.UpdateIntervalInMinutes <= 0)
                {
                    timer.Change(TimeSpan.FromMilliseconds(-1), TimeSpan.FromMilliseconds(-1));
                }
                else
                {
                    timer.Change(TimeSpan.FromMinutes(user.UpdateIntervalInMinutes), TimeSpan.FromMilliseconds(-1));
                }
            }
            finally
            {
                Monitor.Exit(syncRoot);
            }
        }

        public UserProfile User { private set; get; }
        public TaskStatus Status { private set; get; }
        public DateTime LastRun { private set; get; }

        #region private

        private void Run(object state)
        {
            if (this == null || this.Status != TaskStatus.OnSchedule)
            {
                return;
            }

            if (Monitor.TryEnter(this.syncRoot))
            {
                try
                {
                    if (this.Status != TaskStatus.OnSchedule) 
                    {
                        return;
                    }

                    //PostItem item = new PostItem()
                    //{
                    //    User = new StatusUser()
                    //    {
                    //        Name="Jarod",
                    //       ProfileImage = "http://tp1.sinaimg.cn/1916825084/50/1294595605/1",
                    //    },
                    //    CreateTime = "helo",
                    //    Message = "Test",

                    //};

                    //SendNotification(item);

                    ReadPostsAndSendNotification();

                    this.timer.Change(TimeSpan.FromMinutes(this.User.UpdateIntervalInMinutes), TimeSpan.FromMilliseconds(-1));
                }
                finally
                {
                    Monitor.Exit(this.syncRoot);
                }
            }            
        }

        private void ReadPostsAndSendNotification()
        {
            AMicroblogAPI.Environment.AccessToken = new OAuthAccessToken()
            {
                Token = User.AccessToken,
                Secret = User.AccessSecret,
                UserID = User.UserID,
            };

            try
            {
                var statuses = AMicroblogAPI.AMicroblog.GetFriendsStatuses(null, null, null, 20,
                    AMicroblogAPI.DataContract.StatusType.All, false);

                if (statuses.Items.Count == 0)
                {
                    return;
                }

                var latest = statuses.Items.First();

                if (latest.CreatedAt == this.lastPostCreateTime)
                {
                    var e = statuses.Items.GetEnumerator();

                    while (e.MoveNext())
                    {
                        if (e.Current.CreatedAt == this.lastPostShowedCreateTime)
                        {
                            break;
                        }
                    }

                    if (e.MoveNext())
                    {
                        latest = e.Current;
                    }
                    else
                    {
                        latest = null;
                    }
                }
                else
                {
                    this.lastPostCreateTime = latest.CreatedAt;
                    this.lastPostShowedCreateTime = null;
                }

                if (latest != null)
                {
                    this.lastPostShowedCreateTime = latest.CreatedAt;

                    SendNotification(latest);
                }
            }
            catch
            {
            }
        }

        //private void SendCallback(SdkRequestType requestType, SdkResponse response)
        //{
        //    if (response.errCode == SdkErrCode.SUCCESS)
        //    {
        //        XElement statusXml = XElement.Parse(response.content);
        //        var statusCollection = from XElement statusElement in statusXml.Descendants("status")
        //                               select new PostItem(statusElement, statusElement.Element("user"));

        //        var s = statusCollection.First();
        //        if (s.CreateTime == this.lastPostCreateTime)
        //        {
        //            var e = statusCollection.GetEnumerator();

        //            while (e.MoveNext())
        //            {
        //                if (e.Current.CreateTime == this.lastPostShowedCreateTime)
        //                {
        //                    break;
        //                }
        //            }

        //            if (e.MoveNext())
        //            {
        //                s = e.Current;
        //            }
        //            else
        //            {
        //                s = null;
        //            }
        //        }
        //        else
        //        {
        //            this.lastPostCreateTime = s.CreateTime;
        //            this.lastPostShowedCreateTime = null;
        //        }

        //        if (s != null)
        //        {
        //            this.lastPostShowedCreateTime = s.CreateTime;
        //        }
        //    }
        //}

        private void SendNotification(AMicroblogAPI.DataContract.StatusInfo item)
        {
            try{
                string subscriptionUri = this.User.PushChannel;


                HttpWebRequest sendNotificationRequest = (HttpWebRequest)WebRequest.Create(subscriptionUri);
                sendNotificationRequest.Method = "POST";
                
                // The optional custom header X-MessageID uniquely identifies a notification message. 
                // If it is present, the // same value is returned in the notification response. It must be a string that contains a UUID.
                // sendNotificationRequest.Headers.Add("X-MessageID", "<UUID>");

                // Create the tile message.
                string tileMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                "<wp:Notification xmlns:wp=\"WPNotification\">" +
                    "<wp:Tile>" +
                      "<wp:BackgroundImage>" + (item.User.ProfileImageUrl ?? String.Empty) + "</wp:BackgroundImage>" +
                      "<wp:Count>0</wp:Count>" +
                      "<wp:Title>" + item.User.Name + "</wp:Title>" +
                      "<wp:BackBackgroundImage>" + (item.ThumbnailPic ?? String.Empty) + "</wp:BackBackgroundImage>" +
                      "<wp:BackTitle>" + item.CreatedAt.Split(' ')[3] + "</wp:BackTitle>" +
                      "<wp:BackContent>" + (item.Text ?? String.Empty) + "</wp:BackContent>" +
                   "</wp:Tile> " +
                "</wp:Notification>";

                // Sets the notification payload to send.
                byte[] notificationMessage = Encoding.UTF8.GetBytes(tileMessage);

                // Sets the web request content length.
                sendNotificationRequest.ContentLength = notificationMessage.Length;
                sendNotificationRequest.ContentType = "text/xml";
                sendNotificationRequest.Headers.Add("X-WindowsPhone-Target", "token");
                sendNotificationRequest.Headers.Add("X-NotificationClass", "1");


                using (Stream requestStream = sendNotificationRequest.GetRequestStream())
                {
                    requestStream.Write(notificationMessage, 0, notificationMessage.Length);
                }

                // Send the notification and get the response.
                HttpWebResponse response = (HttpWebResponse)sendNotificationRequest.GetResponse();
                string notificationStatus = response.Headers["X-NotificationStatus"];
                string notificationChannelStatus = response.Headers["X-SubscriptionStatus"];
                string deviceConnectionStatus = response.Headers["X-DeviceConnectionStatus"];

                // Display the response from the Microsoft Push Notification Service.  
                // Normally, error handling code would be here.  In the real world, because data connections are not always available,
                // notifications may need to be throttled back if the device cannot be reached.
                // notificationStatus + " | " + deviceConnectionStatus + " | " + notificationChannelStatus;
            }
            catch (Exception ex)
            {
                lastException = ex;
            }
        }

        private Timer timer;
        private object syncRoot;
        
        private string lastPostCreateTime;
        private string lastPostShowedCreateTime;
        private Exception lastException;
        #endregion
    }

}